#pragma once
#include<string>
#include<vector>
#include<iostream>
#include<fstream>
#include<sys/stat.h>
#include<experimental/filesystem>
#include"bundle.h"
#include<jsoncpp/json/json.h>
#include<memory>
namespace Util
{
  namespace fs = std::experimental::filesystem;

  class FileUtil{
    private:
      std::string _filename;
    public:
      FileUtil(const std::string& filename):_filename(filename){}

    



      int64_t FileSize()
      {
        struct stat st;
        if(stat(_filename.c_str(),&st)<0)
        {
          std::cout<<"FileSize err"<<std::endl;
          return -1;
        }
        return st.st_size;
      }
      time_t LastModTime()
      {
        
        struct stat st;
        if(stat(_filename.c_str(),&st)<0)
        {
          std::cout<<"LastModTime err"<<std::endl;
          return -1;
        }
        return st.st_mtime;
      }
      time_t LastAccTime()
      {

        struct stat st;
        if(stat(_filename.c_str(),&st)<0)
        {
          std::cout<<"LastAccTime err"<<std::endl;
          return -1;
        }
        return st.st_atime;
      }
      std::string FileName()
      {
        size_t pos = _filename.find_last_of("/");
        if(pos == std::string::npos)
        {
          return _filename;
        }
        return _filename.substr(pos+1);
      }
      bool SetContent(const std::string& body)
      {
        std::ofstream ofs;
        ofs.open(_filename,std::ios::binary);
        if(ofs.is_open() == false)
        {      
          std::cout << "SetPostLen open err"<<std::endl;
          return false;
        }
        ofs.write(&body[0],body.size());
        if(ofs.good() == false)
        {
          
          std::cout << "SetPostLen write err"<<std::endl;
          ofs.close();
          return false;
        }
        ofs.close();
        return true;
      }
      bool GetContent(std::string* body)
      {
        size_t fsize = FileSize();
        return GetPostLen(body,0,fsize);
      }
      bool GetPostLen(std::string* body, size_t pos,size_t len)
      {
        size_t fsize = this->FileSize();
        if(fsize < pos+len)
        {
          std::cout << "GetPostLen len err"<<std::endl;
          return false;
        }
        std::ifstream ifs;
        ifs.open(_filename,std::ios::binary);
        if(ifs.is_open() == false)
        {
          std::cout <<"GetPostLen open err"<<std::endl;
          return false;
        }
        ifs.seekg(pos,std::ios::beg);
        body->resize(len);
        ifs.read(&(*body)[0],len);
        if(ifs.good() == false)
        {

          ifs.close();
          std::cout << "GetPostLen read err"<<std::endl;
          return false;
        }
        ifs.close();
        return true;
      }
      bool Exists()
      {
        return fs::exists(_filename);
      }
      bool CreateDirectory()
      {
        if(Exists()) return true;
        fs::create_directories(_filename);
      }
      bool ScanDirectory(std::vector<std::string>* arry)
      {
         for(auto& p : fs::directory_iterator(_filename))
         {
           if(fs::is_directory(p)) continue;
           arry->push_back(fs::path(p).relative_path().string());
         }
         return true;
      }
      bool Compress(const std::string& packname)
      {
        std::string body;
        if(GetContent(&body) == false)
        {
          std::cout <<"Compress - GetContent err"<<std::endl;
          return false;
        }
        std::string packed = bundle::pack(bundle::LZIP,body);
        FileUtil cfile(packname);
        if(cfile.SetContent(packed) == false)
        {
          std::cout <<"Compress - SetContent err"<<std::endl;
          return false;
        }
        return true;
      }
      bool UnCompress(const std::string& filename)
      {
        std::string body;

        if(GetContent(&body) == false)
        {
          std::cout <<"Compress - GetContent err"<<std::endl;
          return false;
        }
        std::string unpacked = bundle::unpack(body);
        FileUtil cfile(filename);
        if(cfile.SetContent(unpacked) == false)
        {
          std::cout <<"Compress - SetContent err"<<std::endl;
          return false;
        }
        return true;
      }
      bool Remove()
      {
        if(Exists() == false)
        {
          return false;
        }
        remove(_filename.c_str());
        return true;
      }
  };

  class JsonUtil{
    private:

    public:


      static bool Serialize(const Json::Value &root,std::string* str)
      {
        Json::StreamWriterBuilder swb;
        std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
        std::stringstream ss;
        if(sw->write(root,&ss) != 0)
        {
          std::cout << "Serialize err"<<std::endl;
          return false;
        }
        *str = ss.str();
        return true;
      }
      static bool UnSerialize(Json::Value* root,const std::string& str)
      {
        Json::CharReaderBuilder crb;
        std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
        std::string errs;
        if(cr->parse(str.c_str(),str.c_str()+str.size(),root,&errs) == false)
        {
          std::cout << "UnSerialize err " + errs<<std::endl;
          return false;
        }
        return true;
      }


  };

}
